﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using log4net;
using MsgPack;
using MsgPack.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace OF.DistributeService.Core.Common
{
    public static class Util
    {
        public const string MsgPackContentType = "application/x-msgpack";
        public const string JSONContentType = "application/json";
        private static ILog logger = null;
        private static Type GenericNullableType = typeof(Nullable<>);
        public const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss.fff";

        public static bool SafeLoopUtilTrue(Func<int, bool> func, int maxCount, int sleepMS)
        {
            maxCount = 0;

            int loopI = 0;
            System.Exception lastEx = null;
            do
            {
                try
                {
                    if (func(loopI))
                    {
                        return true;
                    }
                }
                catch (System.Exception ex)
                {
                    lastEx = ex;
                }
                if (sleepMS > 0)
                {
                    Thread.Sleep(sleepMS);
                }
            }
            while (++loopI < maxCount);
            return false;
        }

        public static T MaxRetryCall<T>(Func<T> func, int maxCount, int sleepMS)
        {
            int loopCount = 0;
            System.Exception lastEx = null;
            do
            {
                try
                {
                    return func();
                }
                catch (System.Exception ex)
                {
                    lastEx = ex;
                    if (sleepMS > 0)
                    {
                        Thread.Sleep(sleepMS);
                    }
                }
            }
            while (++loopCount < maxCount);
            throw lastEx;
        }

        public static byte[] GetMsgPackBytes<inT>(inT param)
        {
            MessagePackSerializer<inT> paramSerailizer = SerializationContext.Default.GetSerializer<inT>();
            return paramSerailizer.PackSingleObject(param);
        }

        public static outT GetMsgPackObject<outT>(byte[] btArray)
        {
            MessagePackSerializer<outT> cmdSer = SerializationContext.Default.GetSerializer<outT>();
            return cmdSer.UnpackSingleObject(btArray);
        }

        public static byte[] Compress(byte[] sourceData)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (System.IO.Compression.GZipStream gzip = new System.IO.Compression.GZipStream(ms, CompressionMode.Compress))
                {
                    gzip.Write(sourceData, 0, sourceData.Length);
                    gzip.Close();
                    byte[] compressArray = ms.ToArray();
                    return compressArray;
                }
            }
        }

        public static byte[] DeCompress(byte[] compressArray)
        {
            using (MemoryStream ms = new MemoryStream(compressArray))
            {
                using (System.IO.Compression.GZipStream gzip = new System.IO.Compression.GZipStream(ms, CompressionMode.Decompress))
                {
                    using (MemoryStream destMS = new MemoryStream())
                    {
                        byte[] buffer = new byte[10240];
                        int batchCount = 0;
                        while ((batchCount = gzip.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            destMS.Write(buffer, 0, batchCount);
                        }
                        return destMS.ToArray();
                    }
                }

            }
        }

        public static void ConfigConnection(HttpWebRequest request)
        {            
            request.ServicePoint.ConnectionLimit = 10;
            ServicePointManager.DefaultConnectionLimit = 10;
            request.ServicePoint.SetTcpKeepAlive(true, 5000, 1500);
        }

        public static void attachDebug()
        {
            #if DEBUG
            //only support for debug version!
            if (Debugger.IsAttached)
            {
                Debugger.Break();
            }
            else
            {
                Debugger.Launch();
            }
            #endif
        }

        public static void get()
        { 
            
        }


        public static void SetRequestResponseTaskResult<T>(TaskCompletionSource<T> taskSource, HttpWebRequest request, ISetResponseTaskResult<T> iSetResult)
        {
            request.BeginGetResponse(new AsyncCallback((ar) =>
            {
                try
                {
                    using (WebResponse response = request.EndGetResponse(ar))
                    {
                        using (Stream myResponseStream = response.GetResponseStream())
                        {
                            iSetResult.SetResult(taskSource, myResponseStream);
                        }
                    }
                }
                catch (System.Exception exInner)
                {
                    taskSource.SetException(exInner);
                }
            }), null);
        }

        static Util()
        {
            log4net.Config.XmlConfigurator.Configure();
            logger = LogManager.GetLogger(typeof(Util));
        }

        private static DateTime LastDateTime = DateTime.Now;
        public static void TraceP(string name)
        {
            DateTime newDateTime = DateTime.Now;
            LogInfo(name + ":" + newDateTime.ToString("HH:mm:ss.fff") + " " + newDateTime.Subtract(LastDateTime).TotalMilliseconds);
            LastDateTime = DateTime.Now;
        }

        public static T SafeCastObject<T>(object obj)
        {
            if (obj == null)
            {
                return default(T);
            }
            return (T)obj;
        }

        public const string UnExcepedException = "An unexcepected exception occur, please wait a moment and reload again!";

        public static List<List<T>> SplitGroup<T>(List<T> list, int groupSize)
        {
            List<List<T>> result = new List<List<T>>();
            List<T> currentList = new List<T>();
            foreach (var item in list)
            {
                currentList.Add(item);
                if (currentList.Count >= groupSize)
                {
                    result.Add(currentList);
                    currentList = new List<T>();
                }
            }
            if (currentList.Count > 0)
            {
                result.Add(currentList);
            }
            return result;
        }

        public static void LoopAction(Action action, int sleepIntervalSeconds)
        {
            try
            {
                while (true)
                {
                    try
                    {
                        action();
                    }
                    catch (System.Exception ex)
                    {
                        Util.LogInfo("DeleteLogAction Inner Exception:" + ex.ToString());
                    }
                    SleepNSeconds(sleepIntervalSeconds);
                }
            }
            catch (System.Exception ex)
            {
                SleepNSeconds(1);
                Util.LogInfo("DeleteLogAction Exit Exception:" + ex.ToString());
            }
        }

        public static void SleepNSeconds(int seconds)
        {
            for (int i1 = 0; i1 < seconds; i1++)
            {
                Thread.Sleep(1000);
            }
        }

        public static void EnsureExecute(Action action, int maxSleepSeconds)
        {
            while (true)
            {
                try
                {
                    action();
                    break;
                }
                catch (System.Exception ex)
                {
                    Util.LogInfo("Jpush thread save xml exception:" + ex.ToString());
                    SleepNSeconds(maxSleepSeconds);
                }
            }
        }

        public static byte[] GetMemoryStreamBytes(MemoryStream ms)
        {
            byte[] btArray = new byte[ms.Position];
            Array.Copy(ms.GetBuffer(), btArray, ms.Position);
            return btArray;
        }

        public static string GetBase64JsonString(object obj)
        {
            var str = GetJsonString(obj);
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(str));
        }

        public static Type GetParamTyeIfNullable(Type type)
        {
            if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(GenericNullableType))
            {
                return type.GetGenericArguments()[0];
            }
            else
            {
                return type;
            }
        }

        public static IsoDateTimeConverter GetDateTimeConverter()
        {
            return new Newtonsoft.Json.Converters.IsoDateTimeConverter() { DateTimeFormat = Util.DateTimeFormat };
        }

        public static string GetJsonString(object obj)
        {
            if (obj == null)
            {
                return null;
            }

            return Newtonsoft.Json.JsonConvert.SerializeObject(obj, Formatting.None, GetJsonSetting());
        }

        public static JsonSerializerSettings GetJsonSetting()
        {
            var setting = new Newtonsoft.Json.JsonSerializerSettings
            {
                NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
                DateFormatHandling = DateFormatHandling.IsoDateFormat
            };
            setting.Converters.Add(GetDateTimeConverter());
            return setting;
        }

        public static T GetJsonObject<T>(string str)
        {
            if (str == null)
            {
                return default(T);
            }
            else
            {
                return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(str, GetJsonSetting());
            }
        }

        public static object GetJsonObject(string str, Type type)
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject(str, type, GetJsonSetting());
        }
        public static bool EnumEquals(Enum e1, Enum e2)
        {
            if (e1 == e2)
            {
                return true;
            }
            if (e1 == null || e2 == null)
            {
                return false;
            }
            return e1.Equals(e2);
        }


        public static void LogInfo(string str)
        {
            logger.Info(str);
        }

        public static void LogException(object obj, System.Exception ex)
        {
            string str = string.Empty;
            if (obj != null)
            {
                str = obj.ToString();
            }
            str += ", Exception is:" + System.Environment.NewLine + ex.ToString();
            logger.Error(str);
        }

        public static ExecuteResult GetError(int code, string errorMessage)
        {
            return new ExecuteResult
            {
                IsSuccess = false,
                ErrorCode = code,
                ErrorMessage = errorMessage
            };
        }

        public static ExecuteResult<T> GetError<T>(int code, string errorMessage)
        {
            return new ExecuteResult<T>
            {
                IsSuccess = false,
                ErrorCode = code,
                ErrorMessage = errorMessage
            };
        }


        public static ExecuteResult GetSuccess()
        {
            return new ExecuteResult
            {
                IsSuccess = true
            };
        }

        public static ExecuteResult<T> GetSuccess<T>(T result)
        {
            return new ExecuteResult<T>
            {
                IsSuccess = true,
                Result = result
            };
        }

        public static bool IsEquals(byte[] btArr1, byte[] btArr2)
        {
            if (btArr1 == btArr2)
            {
                return true;
            }
            else
            {
                if (btArr1 == null || btArr2 == null)
                {
                    return false;
                }
            }

            if (btArr1.Length != btArr2.Length)
            {
                return false;
            }

            for (int i1 = 0; i1 < btArr1.Length; i1++)
            {
                if (btArr1[i1] != btArr2[i1])
                {
                    return false;
                }
            }
            return true;
        }
    }

    public class IgnoreCaseComparer : IEqualityComparer<string>
    {
        public bool Equals(string x, string y)
        {
            if (object.ReferenceEquals(x, y))
            {
                return true;
            }

            if (x == null || y == null)
            {
                return false;
            }
            return string.Equals(x, y, StringComparison.InvariantCultureIgnoreCase);
        }

        public int GetHashCode(string obj)
        {
            return obj == null ? 0 : obj.ToLower().GetHashCode();
        }
    }

    /// <summary>
    /// 返回结果
    /// </summary>
    public class ExecuteResult
    {
        /// <summary>
        /// 是否成功
        /// </summary>
        public bool IsSuccess { get; set; }
        /// <summary>
        /// 信息
        /// </summary>
        public string ErrorMessage { get; set; }
        /// <summary>
        /// 错误码
        /// </summary>
        public int ErrorCode { get; set; }

        public int RequestContext { get; set; }
    }

    public class ExecuteResult<T> : ExecuteResult
    {
        /// <summary>
        /// 返回数据
        /// </summary>
        public T Result { get; set; }
    }

    public class JSONContentFormatGetClient<T> : ContentFormatGetClientBase<T>
    {
        public JSONContentFormatGetClient(string methodName, string urlParam) : base(methodName, urlParam)
        {
        
        }

        protected override void InitRequest(HttpWebRequest request)
        {
            request.ContentType = Util.JSONContentType;
            request.Accept = Util.JSONContentType;
            request.ContentLength = 0;
        }

        protected override ISetResponseResult<T> GetSetResponseResult()
        {
            return new JSONSetResponseResult<T>();
        }

        protected override ISetResponseTaskResult<InTaskT> GetSetResponseTaskResult<InTaskT>()
        {
            return new JSONSetResponseTaskResult<InTaskT>();
        }
    }

    public class JSONContentFormatPostClient<T> : ContentFormatPostClientBase<T>
    {
        private byte[] requestBodyContent;

        public JSONContentFormatPostClient(string methodName, string url, Type paramType, object paramObjValue)
            : base(methodName, url, paramType, paramObjValue)
        { 
        }

        protected override void InitRequest(HttpWebRequest request)         
        {
            string postDataStr = Util.GetJsonString(paramObjValue);
            requestBodyContent = Encoding.UTF8.GetBytes(postDataStr);
            request.ContentType = Util.JSONContentType;
            request.Accept = Util.JSONContentType;
            request.ContentLength = requestBodyContent.Length;
        }

        protected override void WriteRequestContent(Stream myRequestStream)
        { 
            myRequestStream.Write(requestBodyContent, 0, requestBodyContent.Length);
        }

        protected override ISetResponseResult<T> GetSetResponseResult()
        {
            return new JSONSetResponseResult<T>();
        }

        protected override ISetResponseTaskResult<InTaskT> GetSetResponseTaskResult<InTaskT>()
        {
            return new JSONSetResponseTaskResult<InTaskT>();
        }
    }

    public abstract class ContentFormatPostClientBase<T>
    {
        protected string methodName;
        protected string url;
        protected Type paramType;
        protected object paramObjValue;
        
        public ContentFormatPostClientBase(string methodName, string url, Type paramType, object paramObjValue)
        { 
            this.methodName = methodName;
            this.url = url;
            this.paramType = paramType;
            this.paramObjValue = paramObjValue;
        }

        protected abstract void InitRequest(HttpWebRequest request);
        protected abstract void WriteRequestContent(Stream myRequestStream);
        protected abstract ISetResponseResult<T> GetSetResponseResult();
        protected abstract ISetResponseTaskResult<InTaskT> GetSetResponseTaskResult<InTaskT>();


        public HttpWebRequest CreateRequest()
        {            
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            Util.ConfigConnection(request);
            request.Method = methodName;
            request.KeepAlive = true;
            InitRequest(request);
            return request;
        }

        public T Post()
        {
            HttpWebRequest request = CreateRequest();
            using (Stream myRequestStream = request.GetRequestStream())
            {
                WriteRequestContent(myRequestStream);
                myRequestStream.Close();
                using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                {
                    using (Stream myResponseStream = response.GetResponseStream())
                    {
                        return GetSetResponseResult().GetResult(myResponseStream);
                    }
                }
            }
        }

        public T PostAsync(Type inTaskType)
        {            
            return Util.SafeCastObject<T>(this.GetType().GetMethod("PostAsyncCore").MakeGenericMethod(new Type[] { inTaskType }).Invoke(this, null));
        }

        public Task<InTaskT> PostAsyncCore<InTaskT>()
        {
            TaskCompletionSource<InTaskT> taskSource = new TaskCompletionSource<InTaskT>();
            try
            {
                HttpWebRequest request = CreateRequest();
                request.BeginGetRequestStream(new AsyncCallback((ar) =>
                {
                    try
                    {
                        using (Stream myRequestStream = request.EndGetRequestStream(ar))
                        {
                            WriteRequestContent(myRequestStream);
                            myRequestStream.Close();
                            Util.SetRequestResponseTaskResult<InTaskT>(taskSource, request, GetSetResponseTaskResult<InTaskT>());
                        }
                    }
                    catch (System.Exception ex2)
                    {
                        taskSource.SetException(ex2);
                    }
                }), null);
            }
            catch (System.Exception ex)
            {
                taskSource.SetException(ex);
            }
            return taskSource.Task;
        }
    }

    public abstract class ContentFormatGetClientBase<T>
    {
        protected string methodName;
        protected string urlParam;

        public ContentFormatGetClientBase(string methodName, string urlParam)
        {
            this.methodName = methodName;
            this.urlParam = urlParam;
        }

        protected abstract void InitRequest(HttpWebRequest request);
        protected abstract ISetResponseResult<T> GetSetResponseResult();
        protected abstract ISetResponseTaskResult<InTaskT> GetSetResponseTaskResult<InTaskT>();

        public T Get()
        {
            HttpWebRequest request = CreateWebRequest();
            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                using (Stream myResponseStream = response.GetResponseStream())
                {
                    return GetSetResponseResult().GetResult(myResponseStream);
                }
            }
        }

        public T GetAsync(Type inTaskType)
        {
            return Util.SafeCastObject<T>(this.GetType().GetMethod("GetAsyncCore").MakeGenericMethod(new Type[] { inTaskType }).Invoke(this, null));
        }

        private HttpWebRequest CreateWebRequest()
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlParam);
            Util.ConfigConnection(request);
            request.Method = methodName;
            request.KeepAlive = true;
            InitRequest(request);
            return request;
        }

        public Task<InTaskType> GetAsyncCore<InTaskType>()
        {
            TaskCompletionSource<InTaskType> taskSource = new TaskCompletionSource<InTaskType>();
            try
            {
                HttpWebRequest request = CreateWebRequest();
                Util.SetRequestResponseTaskResult(taskSource, request, GetSetResponseTaskResult<InTaskType>());
            }
            catch (System.Exception ex)
            {
                taskSource.SetException(ex);
            }
            return taskSource.Task;
        }
    }

    public interface ISetResponseTaskResult<T>
    {
        void SetResult(TaskCompletionSource<T> taskSource, Stream myResponseStream);
    }

    public interface ISetResponseResult<T>
    {
        T GetResult(Stream myResponseStream);
    }

    public class JSONSetResponseResult<T> : ISetResponseResult<T>
    {
        public static T GetResultCore(Stream myResponseStream)
        {
            using (StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8")))
            {
                string retString = myStreamReader.ReadToEnd();
                return Util.GetJsonObject<T>(retString);
            }
        }

        public T GetResult(Stream myResponseStream)
        {
            return GetResultCore(myResponseStream);
        }
    }

    public class JSONSetResponseTaskResult<T> : ISetResponseTaskResult<T>
    {
        public void SetResult(TaskCompletionSource<T> taskSource, Stream myResponseStream)
        {
            taskSource.SetResult(JSONSetResponseResult<T>.GetResultCore(myResponseStream));
        }
    }

    public class MSGPackContentFormatGetClient<T> : ContentFormatGetClientBase<T>
    {
        public MSGPackContentFormatGetClient(string methodName, string urlParam)
            : base(methodName, urlParam)
        {

        }

        protected override void InitRequest(HttpWebRequest request)
        {
            request.ContentType = Util.MsgPackContentType;
            request.Accept = Util.MsgPackContentType;
            request.ContentLength = 0;
        }

        protected override ISetResponseResult<T> GetSetResponseResult()
        {
            return new MSGPackSetResponseResult<T>();
        }

        protected override ISetResponseTaskResult<InTaskT> GetSetResponseTaskResult<InTaskT>()
        {
            return new MSGPackSetResponseTaskResult<InTaskT>();
        }
    }

    public class MSGPackSetResponseResult<T> : ISetResponseResult<T>
    {
        public static T GetResultCore(Stream myResponseStream)
        {
            var serializer = MessagePackSerializer.Get(typeof(T));
            using (var mpUnpacker = Unpacker.Create(myResponseStream))
            {
                mpUnpacker.Read();
                object result = serializer.UnpackFrom(mpUnpacker);
                return Util.SafeCastObject<T>(result);
            }
        }

        public T GetResult(Stream myResponseStream)
        {
            return GetResultCore(myResponseStream);
        }
    }

    public class MSGPackSetResponseTaskResult<T> : ISetResponseTaskResult<T>
    {
        public void SetResult(TaskCompletionSource<T> taskSource, Stream myResponseStream)
        {
            taskSource.SetResult(MSGPackSetResponseResult<T>.GetResultCore(myResponseStream));
        }
    }

    public class MSGPackContentFormatPostClient<T> : ContentFormatPostClientBase<T>
    {
        private byte[] requestBodyContent;
        public MSGPackContentFormatPostClient(string methodName, string url, Type paramType, object paramObjValue)
            : base(methodName, url, paramType, paramObjValue)
        {
        }

        protected override void InitRequest(HttpWebRequest request)
        {
            var serializer = MessagePackSerializer.Get<dynamic>();
            requestBodyContent = serializer.PackSingleObject(paramObjValue);
            request.ContentType = Util.MsgPackContentType;
            request.Accept = Util.MsgPackContentType;
            request.ContentLength = requestBodyContent.Length;
        }

        protected override void WriteRequestContent(Stream myRequestStream)
        {
            myRequestStream.Write(requestBodyContent, 0, requestBodyContent.Length);
        }

        protected override ISetResponseResult<T> GetSetResponseResult()
        {
            return new MSGPackSetResponseResult<T>();
        }

        protected override ISetResponseTaskResult<InTaskT> GetSetResponseTaskResult<InTaskT>()
        {
            return new MSGPackSetResponseTaskResult<InTaskT>();
        }
    }
}
